#ifndef __cb_cstyle_hpp_y787878t6767rr5r569i__
#define __cb_cstyle_hpp_y787878t6767rr5r569i__

#include <cstddef>
#include <functional>
#include <map>
#include <mutex>

namespace hude
{
	//----------------------------------------------------------------------------------------
	template< typename _Ret, typename ... _Args >
	struct cb_entey_meta_t
	{
		typedef typename std::function< _Ret ( _Args ... ) > fn_t;
		typedef _Ret ( * cb_t )( _Args ... );

		fn_t &fn_;
		cb_t cb_;

		cb_entey_meta_t( fn_t& f, cb_t c ) : fn_( f ), cb_( c ) {}
	};

	//----------------------------------------------------------------------------------------
	template< typename _Ret >
	struct cb_entey_meta_t< _Ret, void >
	{
		typedef typename std::function< _Ret ( void ) > fn_t;
		typedef _Ret ( * cb_t )();

		fn_t &fn_;
		cb_t cb_;

		cb_entey_meta_t( fn_t& f, cb_t c ) : fn_( f ), cb_( c ) {}
	};

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Index, typename _Ret, typename ... _Args >
	struct _cb_entey_instance_t
	{
		typedef cb_entey_meta_t< _Ret, _Args ... > meta_t;
		typedef _Ret ( * cb_t )( _Args ... );

		static typename meta_t::fn_t fn__;

		static _Ret cb__( _Args ... __arg ) { return fn__( std::forward< _Args >( __arg ) ... ); }
	};

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Index, typename _Ret >
	struct _cb_entey_instance_t< _Index, _Ret, void >
	{
		typedef cb_entey_meta_t< _Ret, void > meta_t;
		typedef _Ret ( * cb_t )();

		static typename meta_t::fn_t fn__;

		static _Ret cb__() { return fn__(); }
	};

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Index, typename _Ret, typename ... _Args >
	typename _cb_entey_instance_t< _Index, _Ret, _Args ... >::meta_t::fn_t
		_cb_entey_instance_t< _Index, _Ret, _Args ... >::fn__;

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Index, typename _Ret >
	typename _cb_entey_instance_t< _Index, _Ret, void >::meta_t::fn_t
		_cb_entey_instance_t< _Index, _Ret, void >::fn__;

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Index, typename _Ret, typename ... _Args >
	struct cb_entey_t :
		public _cb_entey_instance_t< _Index, _Ret, _Args ... >,
		public cb_entey_meta_t< _Ret, _Args ... >
	{
		typedef _cb_entey_instance_t< _Index, _Ret, _Args ... > base_instance_t;
		typedef cb_entey_meta_t< _Ret, _Args ... > base_meta_t;

		enum { emINDEX = _Index };

		cb_entey_t() : base_meta_t( base_instance_t::fn__, base_instance_t::cb__ ) {}
	};

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Index, typename _Ret, typename ... _Args >
	struct cb_items_t :
		public cb_items_t< _Index - 1, _Ret, _Args ... >
	{
		typedef cb_items_t< _Index, _Ret, _Args ... > this_t;
		typedef cb_entey_t< _Index, _Ret, _Args ... > entey_t;

		entey_t entey_;

		cb_items_t( cb_entey_meta_t< _Ret, _Args ... >** m ) :
			cb_items_t< _Index - 1, _Ret, _Args ... >( m )
			{ m[ _Index ] = static_cast< cb_entey_meta_t< _Ret, _Args ... >* >( &entey_ ); }

	private:
		cb_items_t( const this_t& );
		cb_items_t& operator=( const this_t& );
		cb_items_t( this_t&& );
		cb_items_t& operator=( this_t&& );
	};

	//----------------------------------------------------------------------------------------
	template< typename _Ret, typename ... _Args >
	struct cb_items_t< 0, _Ret, _Args ... > :
		public cb_entey_t< 0, _Ret, _Args ... >
	{
		typedef cb_items_t< 0, _Ret, _Args ... > this_t;
		typedef cb_entey_t< 0, _Ret, _Args ... > entey_t;

		entey_t entey_;

		cb_items_t( cb_entey_meta_t< _Ret, _Args ... >** m )
			{ m[ 0 ] = static_cast< cb_entey_meta_t< _Ret, _Args ... >* >( &entey_ ); }

	private:
		cb_items_t( const this_t& );
		cb_items_t& operator=( const this_t& );
		cb_items_t( this_t&& );
		cb_items_t& operator=( this_t&& );
	};

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Count, typename _Ret, typename ... _Args >
	struct cb_container_t :
		public cb_items_t< _Count - 1, _Ret, _Args ... >
	{
		typedef cb_items_t< _Count - 1, _Ret, _Args ... > base_t;
		typedef cb_entey_meta_t< _Ret, _Args ... > meta_t;

		meta_t* meta_[ _Count ];

		cb_container_t() : base_t( meta_ ) {}
	};

	//----------------------------------------------------------------------------------------
	template< std::uint8_t _Count, typename _Ret, typename ... _Args >
	class cb_cstyle_t
	{
	public:
		typedef cb_container_t< _Count, _Ret, _Args ... > container_t;
		typedef cb_entey_meta_t< _Ret, _Args ... > meta_t;

		typedef typename meta_t::cb_t cb_t;
		typedef typename meta_t::fn_t fn_t;

		typedef typename std::map< cb_t, meta_t* > using_t;

		enum { emCOUNT = _Count };

	public:
		cb_t push( fn_t fn )
		{
			std::lock_guard< std::mutex > al( mutex_ );

			if( using_.size() == emCOUNT )
				return nullptr;

			for( typename std::uint8_t i = 0; i < emCOUNT; ++i )
			{
				meta_t* item = container_.meta_[ i ];

				if( using_.find( item->cb_ ) == using_.cend() )
				{
					using_.insert( std::make_pair( item->cb_, item ) );
					item->fn_ = fn;
					return item->cb_;
				}
			}

			return nullptr;
		}

		void pop( cb_t cb )
		{
			std::lock_guard< std::mutex > al( mutex_ );

			auto itr = using_.find( cb );

			if( itr	!= using_.cend() )
			{
				itr->second->fn_ = typename meta_t::fn_t();
				using_.erase( itr );
			}
		}

		void clean()
		{
			std::lock_guard< std::mutex > al( mutex_ );

			using_.clear();

			for( typename std::uint8_t i = 0; i < emCOUNT; ++i )
			{
				container_.meta_[ i ]->fn_ = nullptr;
			}
		}

	private:
		container_t container_;
		mutable typename std::mutex mutex_;
		using_t using_;

	};
}

#endif // __cb_cstyle_hpp_y787878t6767rr5r569i__
